﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;

using SQLToolbox.Types;


namespace SQLToolbox.Controller.Tools
{
	public class SchemaComparer
	{
		public static SchemaComaprison Compare(Schema left, Schema right, SchemaComaprison cr)
		{

			CompareCollections(left.Tables, right.Tables, SchemaObjectType.Table, cr);
			CompareCollections(left.Views, right.Views, SchemaObjectType.View, cr);
			CompareCollections(left.Procedures, right.Procedures, SchemaObjectType.Procedure, cr);
			CompareCollections(left.UserDefinedFunctions, right.UserDefinedFunctions, SchemaObjectType.UserDefinedFunction, cr);
			CompareCollections(left.UserDefinedDataTypes, right.UserDefinedDataTypes, SchemaObjectType.UserDefinedDataType, cr);
			CompareCollections(left.UserDefinedTypes, right.UserDefinedTypes, SchemaObjectType.UserDefinedType, cr);

			return cr;

		}

		private static void CompareCollections(SchemaObjectsCollection a, SchemaObjectsCollection b,
		SchemaObjectType type, SchemaComaprison cr)
		{
			var dictA = new Dictionary<string, SchemaObject>();
			var dictB = new Dictionary<string, SchemaObject>();

			foreach (var t in a)
			{
				dictA.Add(t.Name, t);
			}

			foreach (var t in b)
			{
				dictB.Add(t.Name, t);
			}


			foreach (string name in dictA.Keys)
			{
				if (dictB.ContainsKey(name))
					cr.Add(CompareObjects(dictA[name], dictB[name]));
				else
					cr.Add(CompareObjects(dictA[name], null));
			}
			foreach (string name in dictB.Keys)
			{
				if (!dictA.ContainsKey(name))
				{
					cr.Add(CompareObjects(null, dictB[name]));
				}
			}
		}

		public static SchemaObjectComparison CompareObjects(SchemaObject a, SchemaObject b)
		{
			if (a == null && b == null)
				return null;
				//throw new ArgumentNullException("a;b", "Both parameters can't be null");

			SchemaObjectComparison res = new SchemaObjectComparison
			{
				Name = (a != null) ? a.Name : b.Name,
				Type = (a != null) ? a.Type : b.Type
			};
			if (a != null)
			{
				res.LeftText = string.Format("{0}.{1}", a.Schema, a.Name);
				res.LeftObject = a;
			}
			else
			{
				res.LeftText = string.Empty;
				res.LeftObject = null;
			}
			if (b == null)
			{
				res.Status = ComparisonStatus.New;
				res.RightText = string.Empty;
				res.ScriptComparisonResult = ScriptComparer.Compare(a.FullScript, null);
			}
			else
			{
				res.RightObject = b;
				res.RightText = string.Format("{0}.{1}", b.Schema, b.Name);
				if (a != null)
				{
					res.ScriptComparisonResult = ScriptComparer.Compare(a.FullScript, b.FullScript);
					if (res.ScriptComparisonResult.AreEqual)
					{
						res.Status = ComparisonStatus.Equal;
					}
					else
					{
						res.Status = ComparisonStatus.Different;
					}
				}
				else
				{
					res.ScriptComparisonResult = ScriptComparer.Compare(null, b.FullScript);
					res.Status = ComparisonStatus.Missing;
				}

			}
			return res;
		}
	/*	public static string CreateMatchScript(SchemaObject source, SchemaObject target)
		{
			if (source == null)
				return string.Format("DROP {0} {1}", target.Type.ToSQLString(),
					(string.IsNullOrEmpty(target.Schema)) ? target.Name : string.Format("{0}.{1}", target.Schema, target.Name));
			if (target == null)
				return source.FullScript.MergeForExecution();

			StringBuilder script = new StringBuilder();
			script.AppendLine(ReplaceCreateWithAlter(source.Script.MergeForExecution()));
			foreach (SchemaObject obj in source.AttachedObjects)
			{
				if (target.AttachedObjects.Contains(obj.Name))
					script.AppendLine(ReplaceCreateWithAlter(obj.Script.MergeForExecution()));
				else
					script.AppendLine(obj.Script.MergeForExecution());
			}
			foreach (SchemaObject obj in target.AttachedObjects)
			{
				if (!source.AttachedObjects.Contains(obj.Name))
				{
					script.AppendLine(string.Format("DROP {0} {1}", obj.Type.ToSQLString(),
						(string.IsNullOrEmpty(obj.Schema)) ? obj.Name : string.Format("{0}.{1}", obj.Schema, obj.Name)));

				}
			}
			return script.ToString();


			//var leftWords = source.Script.Merge().Split(" \r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
			//var rightWords = target.Script.Merge().Split(" \r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
			//var dictLeft = FindCreates(source, leftWords);
			//var dictRight = FindCreates(target, rightWords);

			//foreach (string key in dictLeft.Keys)
			//{
			//    if (dictRight.ContainsKey(key))
			//    {
			//        leftWords[dictLeft[key]] = "ALTER";
			//    }
			//}

			//StringBuilder drops = new StringBuilder();

			//foreach (string key in dictRight.Keys)
			//{
			//    if (!dictLeft.ContainsKey(key))
			//    {
			//        drops.AppendLine(string.Format("DROP {0} {1}",rightWords[dictRight[key]+1],key));
			//    }
			//}
			//StringBuilder script = new StringBuilder();
			//foreach(var word in leftWords)
			//{
			//    script.AppendLine(string.Format("{0} ", word));
			//}
			//script.AppendLine("\r\n");
			//script.AppendLine(drops.ToString());
			//return script.ToString();
		}

		private static string ReplaceCreateWithAlter(string p)
		{
			string ret = string.Empty;
			var index=0;
			if ((index = p.IndexOf("create", StringComparison.OrdinalIgnoreCase)) != -1)
				return string.Format("{0} ALTER {1}", p.Substring(0, index), p.Substring(index + 6));
			return p;
		}

		private static Dictionary<string, int> FindCreates(SchemaObject source, string[] words)
		{
			var dict = new Dictionary<string, int>();

			var creates = words.Count((s) => (StringComparer.OrdinalIgnoreCase.Compare(s, "create") == 0) ? true : false);
			int found = 0;
			for (var index = 0; index < words.Length && found < creates; index++)
			{
				if (StringComparer.OrdinalIgnoreCase.Compare(words[index], "create") == 0)
				{
					dict.Add(words[index + 2], index);
				}
			}
			return dict;
		}*/
	}
}
